﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;

using LanguageDetector.Data;

namespace LanguageDetector.Test
{
    [TestFixture]
    public class DB01_LanguageStatDB_CRUD : IDisposable
    {
        #region test vars + setup/tear downs
        List<LanguageStat> langs = new List<LanguageStat>();
        Random rand = new Random();

        //setup
        public DB01_LanguageStatDB_CRUD()
        {
            langs.Add(new LanguageStat()
            {
                ISO2 = "1",
                Name = "A",
                BiGramTotal = 0,
                IncludeASCII = false,
            });
            langs.Add(new LanguageStat()
            {
                ISO2 = "22222",
                Name = "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
                BiGramTotal = int.MaxValue / 100,
                IncludeASCII = true,
            });
            langs.Add(new LanguageStat()
            {
                ISO2 = "33",
                Name = "CCC CCCCC CCC CCC",
                BiGramTotal = rand.Next(1, int.MaxValue / 100),
                IncludeASCII = true,
            });
        }

        //tear down
        bool disposed = false;
        public void Dispose()
        {
            lock (this)
            {
                if (!disposed)
                {
                    disposed = true;

                    var ids = (from l in langs where l.ID > 0 select l.ID).ToList();
                    if (!ids.IsNullOrEmpty())
                    {
                        var sb = new StringBuilder("delete from language_stat where id in (");
                        ids.ForEach(id => sb.AppendFormat("{0},", id));
                        sb.Length--;
                        sb.Append(");\r\nVacuum;\r\n");
                        var q = new Query() { Text = sb.ToString() };
                        var errors = q.ExecuteNoneReader();
                        Assert.IsEmpty(errors);
                    }
                }
            }
        }
        ~DB01_LanguageStatDB_CRUD()
        {
            Dispose();
        }
        #endregion

        #region helper methods
        QueryResult<LanguageStat> GetByCriteria(LanguageCriteria f)
        {
            Assert.IsNotNull(f);
            var res = LanguageStatDB.Get(f);
            Assert.IsNotNull(res);
            Assert.IsNotEmpty(res);
            Assert.IsEmpty(res.Errors);
            return res;
        }

        int Compare(LanguageStat og, LanguageStat re)
        {
            Assert.IsNotNull(og);
            Assert.IsNotNull(re);
            Assert.AreEqual(og.ID, re.ID);
            Assert.AreEqual(og.ISO2, re.ISO2);
            Assert.AreEqual(og.Name, re.Name);
            Assert.AreEqual(og.BiGramTotal, re.BiGramTotal);
            Assert.AreEqual(og.IncludeASCII, re.IncludeASCII);
            return 1;
        }
        #endregion

        bool inserted = false;
        [Test]
        public void T10_INSERT()
        {
            langs.ForEach(l => Assert.AreEqual(0, l.ID));
            var errors = LanguageStatDB.Save(langs);
            Assert.IsEmpty(errors);
            langs.ForEach(l => Assert.Greater(l.ID, 0));
            inserted = true;
        }

        [Test]
        public void T20_LOAD_BY_ID()
        {
            if (!inserted)
                T10_INSERT();

            var f = new LanguageCriteria() { IDs = (from l in langs select l.ID).ToList() };
            var res = GetByCriteria(f);
            var tested = (from og in langs
                          join re in res on og.ID equals re.ID
                          select Compare(og, re)).Sum();
            Assert.AreEqual(langs.Count, tested);
        }

        [Test]
        public void T21_LOAD_BY_ISO()
        {
            if (!inserted)
                T10_INSERT();

            var f = new LanguageCriteria() { ISOs = (from l in langs select l.ISO2).ToList() };
            var res = GetByCriteria(f);
            var tested = (from og in langs
                          join re in res on og.ISO2 equals re.ISO2
                          select Compare(og, re)).Sum();
            Assert.AreEqual(langs.Count, tested);
        }

        [Test]
        public void T22_LOAD_BY_NAME()
        {
            if (!inserted)
                T10_INSERT();

            var f = new LanguageCriteria() { Names = (from l in langs select l.Name).ToList() };
            var res = GetByCriteria(f);
            var tested = (from og in langs
                          join re in res on og.Name equals re.Name
                          select Compare(og, re)).Sum();
            Assert.AreEqual(langs.Count, tested);
        }

        [Test]
        public void T23_LOAD_MIN_TOTAL()
        {
            if (!inserted)
                T10_INSERT();

            var counts = (from l in langs orderby l.BiGramTotal select l.BiGramTotal).ToArray();
            var f = new LanguageCriteria() { MinTotal = counts[0] + 1 };
            var res = GetByCriteria(f);
            var tested = (from og in langs
                          where og.BiGramTotal >= f.MinTotal
                          join re in res on og.ID equals re.ID
                          select Compare(og, re)).Sum();
            res.ForEach(r => Assert.GreaterOrEqual(r.BiGramTotal, f.MinTotal));
            Assert.AreEqual(counts.Length - 1, tested);
        }

        bool updated = false;
        [Test]
        public void T30_UPDATE()
        {
            if (!inserted)
                T10_INSERT();

            langs.ForEach(l =>
            {
                l.IncludeASCII = !l.IncludeASCII;
                l.BiGramTotal++;
                l.ISO2 = "z" + l.ISO2;
                l.Name = "u" + l.Name;
            });
            var errors = LanguageStatDB.Save(langs);
            Assert.IsEmpty(errors);
            langs.ForEach(l => Assert.Greater(l.ID, 0));

            updated = true;
        }

        [Test]
        public void T40_RELOAD_UPDATED()
        {
            if (!updated)
                T30_UPDATE();

            var f = new LanguageCriteria() { IDs = (from l in langs select l.ID).ToList() };
            var res = GetByCriteria(f);
            var tested = (from og in langs
                          join re in res on og.ID equals re.ID
                          select Compare(og, re)).Sum();
            Assert.AreEqual(langs.Count, tested);
        }

        bool syncInsert = false;
        [Test]
        public void T45_SYNC_INSERT()
        {
            var n = new LanguageStat() { ISO2 = "55667", Name = "st459020", };
            langs.Add(n);
            var errors = LanguageStatDB.Sync(n);
            Assert.IsEmpty(errors);
            Assert.Greater(n.ID, 0);

            syncInsert = true;
        }

        LanguageStat GetLastLangStat()
        {
            if (!syncInsert)
                T45_SYNC_INSERT();

            Assert.IsNotEmpty(langs);
            var n = (from l in langs where l.ID > 0 orderby l.ID descending select l).FirstOrDefault();
            Assert.IsNotNull(n);
            return n;
        }

        [Test]
        public void T46_SYNC_BY_ID()
        {
            var og = GetLastLangStat();
            var n = new LanguageStat() { ID = og.ID };
            var errors = LanguageStatDB.Sync(n);
            Assert.IsEmpty(errors);
            Compare(og, n);
        }

        [Test]
        public void T47_SYNC_BY_ISO2()
        {
            var og = GetLastLangStat();
            var n = new LanguageStat() { ISO2 = og.ISO2 };
            var errors = LanguageStatDB.Sync(n);
            Assert.IsEmpty(errors);
            Compare(og, n);
        }

        [Test]
        public void T48_SYNC_BY_NAME()
        {
            var og = GetLastLangStat();
            var n = new LanguageStat() { Name = og.Name };
            var errors = LanguageStatDB.Sync(n);
            Assert.IsEmpty(errors);
            Compare(og, n);
        }

        bool deleted = false;
        [Test]
        public void T50_DELETE()
        {
            if (!inserted)
                T10_INSERT();

            var ids = (from l in langs where l.ID > 0 select l.ID).ToList();
            var errors = LanguageStatDB.Delete(ids);
            Assert.IsEmpty(errors);

            deleted = true;
        }

        [Test]
        public void T60_RELOAD_DELETED()
        {
            if (!deleted)
                T50_DELETE();

            var f = new LanguageCriteria() { IDs = (from l in langs select l.ID).ToList() };
            var res = LanguageStatDB.Get(f);
            Assert.IsNotNull(res);
            Assert.IsEmpty(res);
            Assert.IsEmpty(res.Errors);
            langs.Clear();
        }
    }
}
